page.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { notFound, useRouter } from 'next/navigation';
  2. import { useClipboard } from '@/hooks';
  3. import clsx from 'clsx';
  4. import { allGuides } from 'contentlayer/generated';
  5. import { escapeHtml, uiUrl } from '@/config/site';
  6. import { dateTemplate, distanceToNow, format } from '@/lib/date';
  7. import CTABannerEmails from '@/components/CTABAnnerEmails';
  8. import Icon from '@/components/Icon';
  9. import TablerSponsorsBanner from '@/components/TablerSponsorsBanner';
  10. import Mdx from '@/components/MDX';
  11. interface GuidePageProps {
  12. params: {
  13. slug: string
  14. }
  15. }
  16. async function getGuideFromParams(params) {
  17. const slug = params.slug || '';
  18. const guide = allGuides.find((guide) => guide.slugAsParams === slug);
  19. if (!guide) {
  20. null;
  21. }
  22. return guide;
  23. }
  24. export async function generateStaticParams(): Promise<
  25. GuidePageProps['params'][]
  26. > {
  27. return allGuides.map((guide) => ({
  28. slug: guide.slugAsParams,
  29. }));
  30. }
  31. export default async function GuidePage({ params }: GuidePageProps) {
  32. const guide = await getGuideFromParams(params);
  33. if (!guide) {
  34. notFound();
  35. }
  36. const url = `${uiUrl}${guide.slug}`;
  37. // const router = useRouter(),
  38. // currentPath = router.pathname,
  39. // url = `${uiUrl}${guide.slug}`
  40. // clipboard = useClipboard()
  41. return (
  42. <>
  43. <section
  44. className="section"
  45. itemScope={true}
  46. itemType="https://schema.org/NewsArticle"
  47. >
  48. <div className="container">
  49. {/* {meta.date && (
  50. <div
  51. className="text-muted mb-3 font-h6"
  52. itemProp="datePublished"
  53. content={format(meta.date, "yyyy-MM-dd")}
  54. >
  55. Published{" "}
  56. <time dateTime={dateTemplate(meta.date)} className="text-muted">
  57. {distanceToNow(meta.date)}
  58. </time>
  59. </div>
  60. )} */}
  61. <h1 className="h0 mb-3" itemProp="headline">
  62. {guide.title}
  63. </h1>
  64. <div className="row">
  65. <div className="md:col-9 text-muted font-h4">{guide.summary}</div>
  66. </div>
  67. <div className="mt-6">
  68. <div className="row">
  69. <div className="md:col-8">
  70. {guide.image && (
  71. <div className="mb-6">
  72. <img
  73. src={guide.image}
  74. alt={guide.title}
  75. className="border-light rounded"
  76. // lazy={true}
  77. itemProp="image"
  78. />
  79. </div>
  80. )}
  81. <article className="markdown">
  82. <Mdx code={guide.body.code} />
  83. </article>
  84. <TablerSponsorsBanner className="mt-7" />
  85. </div>
  86. <div className="md:col-3 md:order-first">
  87. {guide.tags && (
  88. <div className="tags-list mt-3">
  89. {guide.tags.map((tag) => (
  90. <div className="tag" key={tag}>
  91. {tag}
  92. </div>
  93. ))}
  94. </div>
  95. )}
  96. <div
  97. itemProp="author"
  98. itemScope={true}
  99. itemType="https://schema.org/Person"
  100. >
  101. <div
  102. className="avatar avatar-xl avatar-rounded mb-3"
  103. style={{
  104. backgroundImage: 'url(/img/authors/codecalm.jpg)',
  105. }}
  106. ></div>
  107. <div className="h4" itemProp="name">
  108. Paweł Kuna
  109. </div>
  110. <div className="text-muted">Founder of Tabler</div>
  111. <meta itemProp="url" content={uiUrl} />
  112. </div>
  113. <div className="btn-list mt-6">
  114. <button
  115. type="button"
  116. className={clsx(
  117. 'btn btn-icon btn-circle'
  118. // clipboard.copied ? "btn-green" : ""
  119. )}
  120. aria-label="Copy the Canonical link"
  121. // onClick={() => clipboard.copy(url)}
  122. >
  123. {/* {clipboard.copied ? (
  124. <Icon name="check" />
  125. ) : (
  126. <Icon name="link" />
  127. )} */}
  128. </button>
  129. <a
  130. className="btn btn-icon btn-circle"
  131. target="_blank"
  132. rel="noopener"
  133. href={`https://twitter.com/share?url=${escapeHtml(
  134. url
  135. )}&text=${escapeHtml(guide.title)}`}
  136. >
  137. <Icon name="brand-twitter" />
  138. </a>
  139. <a
  140. className="btn btn-icon btn-circle"
  141. target="_blank"
  142. rel="noopener"
  143. href={`https://www.facebook.com/sharer/sharer.php?u=${escapeHtml(
  144. url
  145. )}`}
  146. >
  147. <Icon name="brand-facebook" />
  148. </a>
  149. <a
  150. className="btn btn-icon btn-circle"
  151. target="_blank"
  152. rel="noopener"
  153. href={`https://www.linkedin.com/share?url=${escapeHtml(
  154. url
  155. )}`}
  156. >
  157. <Icon name="brand-linkedin" />
  158. </a>
  159. </div>
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. </section>
  165. <section className="section bg-light">
  166. <div className="container">
  167. <CTABannerEmails />
  168. </div>
  169. </section>
  170. </>
  171. );
  172. }